﻿using System;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Linq;
using System.Linq.Expressions;
using System.Threading.Tasks;
using Dpx.Models;
using Dpx.Services;
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Command;
using GalaSoft.MvvmLight.Messaging;

namespace Dpx.ViewModels {
    /// <summary>
    /// 诗词搜索页ViewModel。
    /// </summary>
    public class QueryPageViewModel : ViewModelBase {
        /******** 私有方法 ********/

        /// <summary>
        /// 获得搜索条件ViewModel的查询语句。
        /// </summary>
        private static Expression GetExpression(ParameterExpression parameter,
            FilterViewModel filterViewModel) {
            // parameter => p

            // p.Name or p.AuthorName or p.Content
            var property = Expression.Property(parameter,
                filterViewModel.Type.PropertyName);

            // .Contains()
            var method =
                typeof(string).GetMethod("Contains", new[] {typeof(string)});

            // "something"
            var condition =
                Expression.Constant(filterViewModel.Content, typeof(string));

            // p.Name.Contains("something")
            // or p.AuthorName.Contains("something")
            // or p.Content.Contains("something")
            return Expression.Call(property, method, condition);
        }

        /******** 绑定命令 ********/

        /// <summary>
        /// 页面显示命令。
        /// </summary>
        public RelayCommand PageAppearingCommand =>
            _pageAppearingCommand ??= new RelayCommand(
                PageAppearingCommandFunction);

        /// <summary>
        /// 页面显示命令。
        /// </summary>
        private RelayCommand _pageAppearingCommand;

        internal void PageAppearingCommandFunction() {
            if (PoetryQuery == null)
                return;

            FilterViewModelCollection.Clear();
            FilterViewModelCollection.Add(new FilterViewModel(this) {
                Type = FilterType.NameFilter,
                Content = PoetryQuery.Name
            });
            FilterViewModelCollection.Add(new FilterViewModel(this) {
                Type = FilterType.AuthorNameFilter,
                Content = PoetryQuery.AuthorName
            });
            PoetryQuery = null;
        }

        /// <summary>
        /// 搜索命令。
        /// </summary>
        public RelayCommand QueryCommand =>
            _queryCommand ??= new RelayCommand(async () =>
                await QueryCommandFunction());

        /// <summary>
        /// 搜索命令。
        /// </summary>
        private RelayCommand _queryCommand;

        public async Task QueryCommandFunction() {
            // Connection.Table<Poetry>().Where(
            //      p => p.Name.Contains("something")
            //      && p.AuthorName.Contains("something")
            //      && p.Content.Contains("something")
            // ).ToList();

            // p => p.Name.Contains("something")
            //      && p.AuthorName.Contains("something")
            //      && p.Content.Contains("something")

            // p
            var parameter = Expression.Parameter(typeof(Poetry), "p");

            var aggregatedExpression = FilterViewModelCollection
                // Those ViewModels who do have a content.
                .Where(p => !string.IsNullOrWhiteSpace(p.Content))
                // Translate a FilterViewModel to a condition
                // e.g. FilterViewModel {
                //     FileType = {
                //         Name = "作者",
                //         PropertyName = "AuthorName"
                //     },
                //     Content = "苏轼"
                // } => p.AuthorName.Contains("苏轼")
                .Select(p => GetExpression(parameter, p))
                // Put all the conditions together
                // e.g. true && p.AuthorName.Contains("苏轼") && p.Content.Contains("老夫")
                .Aggregate(Expression.Constant(true) as Expression,
                    Expression.AndAlso);

            // Turning the expression into a lambda expression
            var where =
                Expression.Lambda<Func<Poetry, bool>>(aggregatedExpression,
                    parameter);

            await _contentNavigationService.NavigateToAsync(ContentNavigationConstant.ResultPage, where);
        }


        /******** 公开方法 ********/

        /// <summary>
        /// 添加搜索条件ViewModel。
        /// </summary>
        public virtual void AddFilterViewModel(FilterViewModel filterViewModel) =>
            FilterViewModelCollection.Insert(
                FilterViewModelCollection.IndexOf(filterViewModel) + 1,
                new FilterViewModel(this));

        /// <summary>
        /// 删除搜索条件ViewModel。
        /// </summary>
        public virtual void RemoveFilterViewModel(FilterViewModel filterViewModel) {
            FilterViewModelCollection.Remove(filterViewModel);
            if (FilterViewModelCollection.Count == 0) {
                FilterViewModelCollection.Add(new FilterViewModel(this));
            }
        }

        /******** 绑定属性 ********/

        /// <summary>
        /// 诗词查询。
        /// </summary>
        public PoetryQuery PoetryQuery {
            get => _poetryQuery;
            set => Set(nameof(PoetryQuery), ref _poetryQuery, value);
        }

        /// <summary>
        /// 诗词查询。
        /// </summary>
        private PoetryQuery _poetryQuery;

        /// <summary>
        /// 搜索条件ViewModel集合。
        /// </summary>
        public ObservableCollection<FilterViewModel> FilterViewModelCollection { get; } =
            new ObservableCollection<FilterViewModel>();

        /******** 构造函数 ********/

        /// <summary>
        /// 内容导航服务。
        /// </summary>
        private readonly IContentNavigationService _contentNavigationService;

        /// <summary>
        /// 诗词搜索页ViewModel。 
        /// </summary>
        /// <param name="contentNavigationService">内容导航服务。</param>
        public QueryPageViewModel(
            IContentNavigationService contentNavigationService) {
            _contentNavigationService = contentNavigationService;
            FilterViewModelCollection.Add(new FilterViewModel(this));
        }
    }
}